FastAPI 依赖注入与请求处理机制

v2 重构记录:旧版本使用 http.server.ThreadingHTTPServer(stdlib),BaseHandler → TaskAPIHandler 继承链。v0.2 迁移到 FastAPI + Uvicorn,采用 ASGI 异步架构,依赖注入替代继承。

1. 架构变更概览

方面v0.1(旧)v0.2(新)
Web 框架http.server.BaseHTTPRequestHandlerFastAPI ASGI 框架
服务器ThreadingHTTPServerUvicorn (uvloop + httptools)
实例化框架为每个请求 new handler 实例FastAPI 内部路由分发 + Depends()
路由正则匹配 self.path装饰器 @router.get("/{task_id}")
数据库JSON 文件 load_tasks()/save_tasks()SQLAlchemy 2.0 async + SQLite
依赖管理继承覆盖 handle_api()Depends(get_db) 依赖注入
API 文档无自动 OpenAPI /docs
数据验证无Pydantic v2 Schema

2. 请求处理时序

请求处理时序图
FastAPI 请求处理链路:Uvicorn → Starlette → FastAPI Router → 依赖注入 → 业务逻辑

3. FastAPI 依赖注入

3.1 依赖链

FastAPI 使用 Depends() 声明式注入依赖链:

# app/dependencies.py
from typing import Annotated
from fastapi import Depends
from app.database import get_db

DBSession = Annotated[AsyncSession, Depends(get_db)]

# app/routers/tasks.py
@router.get("/{task_id}")
async def get_task(task_id: str, db: DBSession):
    task = await task_service.get_task_by_id(db, task_id)
    ...

请求进来时,FastAPI 解析路由参数 → 调用 get_db() 创建数据库会话 → 注入到 db 参数 → 响应结束后自动关闭会话。

3.2 依赖注入 vs 继承模式对比

维度旧(继承)新(DI)
代码位置self.handle_api() 覆盖基类方法函数签名声明 db: DBSession
测试需要创建完整 handler 实例替换 dependency_overrides即可
可组合性单继承,复杂时需多级继承任意组合多个 Depends
类型安全无类型信息完整 Pydantic 类型验证
生命周期请求结束实例销毁会话自动 close()(context manager)

4. 应用生命周期

FastAPI 使用 lifespan 上下文管理器管理应用启动/关闭:

# app/main.py
@asynccontextmanager
async def lifespan(app: FastAPI):
    await init_db()   # 启动时:创建表
    yield             # 运行中...
    # 关闭时清理资源(如有)

app = FastAPI(title="Task System API", version="0.2.0", lifespan=lifespan)

对比旧版本:旧版本在 server.py:main() 中简单启动 serve_forever(),无生命周期管理。

5. 核心代码

app/main.py — 应用入口

@asynccontextmanager
async def lifespan(app: FastAPI):
    await init_db()
    yield

app = FastAPI(title="Task System API", version="0.2.0", lifespan=lifespan)
app.add_middleware(CORSMiddleware, allow_origins=["*"], ...)
app.include_router(tasks_router)
app.mount("/assets", StaticFiles(directory=FRONTEND_DIST / "assets"))

def main():
    uvicorn.run("app.main:app", host="0.0.0.0", port=8088)

app/database.py — 数据库会话

engine = create_async_engine(settings.database_url, echo=False)
async_session = async_sessionmaker(engine, class_=AsyncSession, expire_on_commit=False)

async def get_db() -> AsyncSession:
    async with async_session() as session:
        try:
            yield session
        finally:
            await session.close()

app/routers/tasks.py — API 路由

router = APIRouter(prefix="/api/tasks", tags=["tasks"])

@router.get("", response_model=list[TaskResponse])
async def list_tasks(db: DBSession, department: Optional[str] = Query(None)):
    return await task_service.get_tasks(db, department=department)

@router.post("/{task_id}/notes", response_model=TaskResponse)
async def add_note(task_id: str, data: TaskUpdateNote, db: DBSession):
    task = await task_service.add_update_note(db, task_id, data)
    if not task:
        raise HTTPException(status_code=404, detail="Task not found")
    return task
测试覆盖验证:通过 app.dependency_overrides[get_db] = override 可替换数据库会话为 SQLite 内存库,11 个自动化测试全部通过(0.08s)。